/**
 * MIT License
 * 
 * Copyright (c) 2024 - present @ ebraid
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <gd32f4.h>
#include <config.h>
#include <mm/mem.h>

/**
 * SYSCLOCK = CLOCK_PLL_OUT_HZ
 * AHB1_HZ = CLOCK_PLL_OUT_HZ / (CLOCK_AHB_PRE_DIV)
 * AHB3_HZ = AHB2_HZ = AHB1_HZ
 * APB1_HZ = CLOCK_PLL_OUT_HZ / (CLOCK_APB1_PRE_DIV)
 * APB2_HZ = CLOCK_PLL_OUT_HZ / (CLOCK_APB2_PRE_DIV)
 * 
 * I2S_HZ = CLOCK_PLL_OUT_HZ / CLOCK_PLL_DIV_Q
 * 
 * AHB1 MAX 240MHz
 * AHB2 MAX 240MHz
 * AHB3 MAX 240MHz
 * 
 * APB1 MAX 60MHz
 * APB2 MAX 120MHz
 * 
 */


#define  CLOCK_HSE_OSC_HZ            (25000000)
#define  CLOCK_PLL_DIV_M             (25)
#define  CLOCK_PLL_MUL_N             (400)
#define  CLOCK_PLL_DIV_P             (2)
#define  CLOCK_PLL_DIV_Q             (4)
#define  CLOCK_PLL_DIV_R             (2)
#define  CLOCK_PLL_OUT_HZ            (200000000)
#define  CLOCK_PLL_IN_HZ             (CLOCK_HSE_OSC_HZ / CLOCK_PLL_DIV_M * CLOCK_PLL_MUL_N)
#define  CLOCK_AHB_PRE_DIV           (0) // 0: no division, 4: /2     5: /4    6: /8     7: /16
#define  CLOCK_APB1_PRE_DIV          (5) // 0: no division, 4: /2     5: /4    6: /8     7: /16
#define  CLOCK_APB2_PRE_DIV          (4) // 0: no division, 4: /2     5: /4    6: /8     7: /16


/**
 *  @brief  SRAM memory information
*/
#define  SRAM_MAIN_START_ADDR             (0x20000000)
#define  SRAM_MAIN_SIZE                   (192 * 1024)
#define  SRAM_TCM_START_ADDR              (0x10000000)
#define  SRAM_TCM_SIZE                    (64 * 1024)


uint32_t system_core_clock = 200000000;


static void clock_init(void)
{
	u16 retry=0;

	RCC->CR |= 0x00000001;		    //设置HISON,开启内部高速RC振荡
	RCC->CFGR = 0x00000000;		    //CFGR清零 
	RCC->CR &= 0xFEF6FFFF;		    //HSEON,CSSON,PLLON清零 
	RCC->PLLCFGR = 0x24003010;	    //PLLCFGR恢复复位值 
	RCC->CR &= ~(1<<18);			//HSEBYP清零,外部晶振不旁路
	RCC->CIR = 0x00000000;		    //禁止RCC时钟中断

	RCC->CR |= 1<<16;				//HSE开启 
	while(((RCC->CR & (1<<17)) ==0 ) && (retry < 0x1FFF)) {
		retry ++;                   //等待HSE RDY
	}

	if(retry != 0x1FFF) {
		RCC->APB1ENR |= 1<<28;	     //电源接口时钟使能
		PWR->CR |= 3<<14; 		     //高性能模式,时钟可到168Mhz

		RCC->CFGR   |= (CLOCK_AHB_PRE_DIV << 4 )  |
		               (CLOCK_APB1_PRE_DIV << 10) | 
					   (CLOCK_APB2_PRE_DIV << 13);

		RCC->CR &= ~(1<<24);	     //关闭主PLL
		
		RCC->PLLCFGR = CLOCK_PLL_DIV_M                         |
					   (CLOCK_PLL_MUL_N << 6)                  | 
					   (((CLOCK_PLL_DIV_P >> 1 ) - 1) << 16)   |
					   (CLOCK_PLL_DIV_Q << 24)                 |
					   (1<<22);

		RCC->CR |= 1<<24;			       //打开主PLL
		while((RCC->CR & (1<<25)) == 0);   //等待PLL准备好

		FLASH->ACR |= 1<<8;		    //指令预取使能.
		FLASH->ACR |= 1<<9;		    //指令cache使能.
		FLASH->ACR |= 1<<10;		//数据cache使能.
		FLASH->ACR |= 5<<0;		    //5个CPU等待周期. 
		RCC->CFGR  &= ~(3<<0);		//清零
		RCC->CFGR  |= 2<<0;		    //选择主PLL作为系统时钟	

		while((RCC->CFGR & (3 << 2)) != ( 2 << 2));//等待主PLL作为系统时钟成功. 
	}

    SCB->VTOR = 0 | (0x0 &(u32)0xFFFFFE00);
}


extern size_t eb_system_heap;

static void heap_init(void)
{
    void* start = &eb_system_heap;
	kmem_init(start, CONFIG_SRAM_SIZE - CONFIG_STACK_SIZE - ((uint32_t)start - SRAM_MAIN_START_ADDR));
    kmem_add_pool((void*)SRAM_TCM_START_ADDR, SRAM_TCM_SIZE);
}


void eb_machine_init(void)
{
	clock_init();
	heap_init();
}
